<?php

/**
 * Description of Abstract
 *
 * @author Admin
 */
class Grabber_Abstract
{
	const NEXT_PARSING_LIST = 7;
	
	protected $_count_grab_news = 1;
	protected $_translate_text  = true;
	protected $_convertToUtf8   = false;

	protected $_allowed_tags = '<p><img><b><u><i><ol><ul><li>';
	
	protected $_db = null;
	protected $_oNews = null;
	protected $_oParser = null;
	protected $_oComment = null;
	
	protected $_oUsers = null;
	protected $_oLinks = null;
	protected $_oComments = null;
	protected $_oBrowser = null;
	protected $_oProxy = null;
	
	protected $_oTranslate = null;

	public function __construct( $db, Parser $oParser, Parser_News $oNews, Parser_Comment $oComment, Browser $oBrowser, Proxy $oProxy = null )
	{
		$this->_db = $db;
		$this->_oNews    = $oNews;
		$this->_oParser  = $oParser;
		$this->_oComment = $oComment;
	
		$this->_oBrowser = $oBrowser;
		$this->_oProxy   = $oProxy;
		$this->_oUsers   = new Users($db);
		$this->_oLinks   = new Links($db);
		$this->_oComments= new Comments( $db );
		
		$this->_oTranslate = new GTranslate( $this->_oProxy );
		
		$this->_init();
	}

	public function _init()
	{
//		throw new Exception('Переоприделите метод _init');
	}
	
	/**
	 * Вызывает ф-цию которая парсит и вставляет записи из списка новостей
	 */
	public function parsingList()
	{
		$parser_data = $this->_oParser->getParserForUpdate();

		if( $parser_data->id )
		{
			$this->_recursiveParsingList( $parser_data );
			$this->_oParser->update( $parser_data->id, false, false, false, $this->_sql_date(self::NEXT_PARSING_LIST) );
		}
	}
	
	protected function _parsingList( $html )
	{
//		throw new Exception('Переоприделите функцию _parsingList');
	}
	
	protected function _nextListPage( $html )
	{
//		throw new Exception('Переоприделите функцию _nextListPage');
	}
	
	protected function _getNews( $html, $news_row = null )
	{
//		throw new Exception('Переоприделите функцию _getNews');
	}

	protected function _toUtf8( $html, $in_charset = null )
	{
		$html = mb_convert_encoding($html, 'UTF-8', $in_charset);
		
		return $html;
	}


	/**
	 * Рекурсивно проходит по страницам и достает линки на новости
	 *
	 * @param object $parser_data 
	 */
	protected function _recursiveParsingList( $parser_data )
	{
		$html = $this->_oBrowser->get( $parser_data->url );

		if( $this->_convertToUtf8 )
		{
			$html = $this->_toUtf8( $html, $this->_convertToUtf8 );
		}

		$news = $this->_parsingList( $html );
		
		if( !empty( $news ) )
		{
			foreach( $news as $title => $url )
			{
				if( !$this->_oNews->check( $this->_stripTags( $title ) ) )
				{
					$this->_oNews->insert( $parser_data->id, 0, $this->_stripTags( $title ), $url, $this->_sql_date(), 1 );
				}
			}
		}

		$next_page = $this->_nextListPage( $html );
		if( $next_page )
		{
			$parser_data->url = $next_page;
			$this->_recursiveParsingList( $parser_data );
		}
	}
	
	/**
	 * Обновляет пропаршеные новости
	 */
	public function parsingUpdateNews()
	{
		$news_for_parsing = $this->_oNews->getNewsForParsing( $this->_count_grab_news, 0 );

		$this->_parsingNews( $news_for_parsing );
	}
	
	/**
	 * Парсит новости
	 */
	public function parsingNews()
	{
		$news_for_parsing = $this->_oNews->getNewsForParsing( $this->_count_grab_news );

		$this->_parsingNews( $news_for_parsing );
	}
	
	/**
	 * Парсит новости по пришедшему урлу
	 *
	 * @param object $news_for_parsing 
	 */
	protected function _parsingNews( $news_for_parsing )
	{
		$_used_users = array();
		
		foreach( $news_for_parsing as $key => $news_row )
		{
//			print_r($news_row); exit;
			$parser_row = $this->_oParser->get( array('id = ' => $news_row->parser_id) );

			$html = $this->_oBrowser->get( $news_row->url );
//			echo $html; exit;
			if( $this->_convertToUtf8 )
			{
				$html = $this->_toUtf8( $html, $this->_convertToUtf8 );
			}
			
			$news = $this->_getNews( $html, $news_row );

			$news_id = $this->_insertNews( $parser_row, $news_row, $news );

			$need_parsing = 0;
			if( $news_row->need_parsing == 0 )
				$need_parsing = 1;
			
			$this->_oNews->update( $news_row->id, false, $news_id, false, false, false, $need_parsing);
		}
	}
	
	/**
	 * Вырезает ненужные пробелы между тэгами
	 *
	 * @param string $text
	 * @return string
	 */
	protected function _cutSpaces( $text )
	{
		$text = mb_eregi_replace('>[\r\n\s]*<', '><', $text);
		$text = mb_eregi_replace('[\r\n\s]+', ' ', $text);
		$text = mb_eregi_replace('>[\r\n\s]+', '>', $text);
		$text = mb_eregi_replace('[\r\n\s]+<', '<', $text);
		return $text;
	}
	
	/**
	 * Вырезает запрещенные теги в тексте
	 * В тэгах вырезаются запрещенные атрибуты
	 *
	 * @param string $text
	 * @return string
	 */
	protected function _stripTags( $text )
	{
		$text = strip_tags( $text, $this->_allowed_tags );
		$text = $this->_stripTagsAttributes($text, $this->_allowed_tags, 'src,href,type');
		$text = mb_eregi_replace('(&#?\w+;)*', '', $text);
		//$text = mb_eregi_replace('[«»]*', '', $text);
		$text = mb_eregi_replace('\s{2,}|\t+|\n+', ' ', $text);
		
		return $text;
	}
	
	/**
	 * В тэгах вырезаются запрещенные атрибуты
	 * 
	 * @param string $string
	 * @param string | array $allowtags
	 * @param string | array $allowattributes
	 * @return string 
	 */
	protected function _stripTagsAttributes( $string, $allowtags=NULL, $allowattributes=NULL )
	{ 
		$string = strip_tags($string,$allowtags); 
		if (!is_null($allowattributes)) { 
			if(!is_array($allowattributes)) 
				$allowattributes = explode(",",$allowattributes); 
			if(is_array($allowattributes)) 
				$allowattributes = implode(")(?<!",$allowattributes); 
			if (strlen($allowattributes) > 0) 
				$allowattributes = "(?<!".$allowattributes.")"; 
			$string = preg_replace_callback("/<[^>]*>/i",create_function( 
				'$matches', 
				'return preg_replace("/ [^ =]*'.$allowattributes.'=(\"[^\"]*\"|\'[^\']*\')/i", "", $matches[0]);'    
			),$string); 
		} 
		
		return $string; 
	} 
	
	/**
	 * Парсит в тексте по паттерну
	 *
	 * @param string $pattern
	 * @param string $text
	 * @return array | boolean
	 */
	protected function _match( $pattern, $text )
	{
		$matched = array();
		
		if( !preg_match_all('|' . $pattern . '|iU', $text, $matched) )
			return false;
		
		return $matched;
	}
	
	/**
	 * Генерирует дату и если нужно добавляет к дате указаное количество дней
	 *
	 * @param int $add_day
	 * @return string 
	 */
	protected function _sql_date( $add_day = false )
	{
		$time = time();
		if( is_numeric( $add_day ) )
		{
			$time += 60 * 60 * 24 * $add_day; 
		}
		
		return date( 'Y-m-d', $time );
	}
	
	/**
	 * Генерирует дату и время и если нужно добавляет к дате указаное количество дней или часов
	 *
	 * @param int $add_day
	 * @param int $add_hourt
	 * @param time $time
	 * @return string 
	 */
	protected function _sql_datetime( $add_day = false, $add_hourt = false, $time = false )
	{
		if( !$time )
			$time = time();

		if( is_numeric( $add_day ) )
		{
			$time += 60 * 60 * 24 * $add_day; 
		}
		
		if( is_numeric( $add_hourt ) )
		{
			$time += 60 * 60 * $add_hourt; 
		}

		return date( 'Y-m-d H:i:s', $time );
	}
	
	/**
	 * Генерирует случайное время.
	 * Если параметр не передан то время генерируется относительно time()
	 *
	 * @param string $time
	 * @return string 
	 */
	protected function _randTime( $time )
	{
		$rand = 60 * rand(rand( 2, 17 ), 60) * rand(1, 24) * rand(1, 5);
		
		if( $time )
			return $time + $rand;
		
		return time() - $rand * 15;
	}
	
	/**
	 * Вставляет записи (link, comment, vote, karma) в базу и коректирует карму голоса
	 *
	 * @param type $parser_row object
	 * @param type $news_row object
	 * @param type $news array
	 * @return id_link
	 */
	protected function _insertNews( $parser_row, $news_row, $news )
	{
		if( empty( $news['news'] ) || empty( $news['title'] ) )
			return false;

		$is_update = ( $news_row->item_id > 1 && $news_row->need_parsing == 0 );
		
		$time = $this->_randTime();

		$user_id = $this->_oUsers->getRandomUser( $_used_users );
		$_used_users[] = $user_id;

		$_title = $this->_stripTags( $news['title'] );
		$_text  = $this->_stripTags( $news['news'] );

		if( $this->_translate_text )
		{
			$_title = $this->_oTranslate->russian_to_english( $_title );
			$_text  = $this->_oTranslate->russian_to_english( $_text );
		}
		
		$link_id = 0;
		if( $is_update )
		{
			$link_id = $this->_oLinks->update( $news_row->item_id, false, false, $_title, $_text, false, false, count($news['comments']), false, $this->_sql_datetime(false, false, $time) );
			$this->_oNews->update( $news_row->id, false, false, $this->_stripTags( $news['title'] ), false, $this->_sql_date(), false );
		}
		else
			$link_id = $this->_oLinks->insert( $user_id, $parser_row->group_id, $_title, $_text, /*$news_row->url*/'', '', count($news['comments']),'', $this->_sql_datetime(false, false, $time) );
		
		if( !$is_update )
		{
			$this->_oUsers->updateKarma( $user_id, 'review' );
			$this->_oUsers->updateKarma( $user_id, 'vote' );
		}
		
		if( !empty( $news['comments'] ) )
		{
			foreach( $news['comments'] as $comment )
			{
				$user_id = $this->_oUsers->getRandomUser( $_used_users );
				$_used_users[] = $user_id;
				
				$time = $this->_randTime( $time );
				
				$rows = $this->_oComment->check( $this->_stripTags( $comment ) );
				if( !$rows )
				{
					$_comment = $this->_stripTags( $comment );
					if( $this->_translate_text )
					{
						$_comment = $this->_oTranslate->russian_to_english( $_comment );
					}
					
					$comment_id = $this->_oComments->insert( $user_id, $link_id, $_comment, $this->_sql_datetime( false, false, $time ) );
					$this->_oComment->insert( $parser_row->id, $news_row->id, $comment_id, $this->_stripTags( $comment ) );
					$this->_oUsers->updateKarma( $user_id, 'comment' );
					$this->_oUsers->updateKarma( $user_id, 'vote' );
				}
			}
		}
//		echo $link_id;
		return $link_id;
	}
	
}

?>